package db

import (
	"apiManageGo/src/core/common/config"
	"apiManageGo/src/core/common/logs"
	"bytes"
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"strconv"
	"sync"
	"text/template"
	"time"
)

var db *gorm.DB
var once sync.Once
var getDbConnErr error

type customWriter struct {
	syncer zapcore.WriteSyncer
}

func (cw *customWriter) Printf(format string, v ...interface{}) {
	// 将格式化后的字符串写入 syncer
	cw.syncer.Write([]byte(fmt.Sprintf(format, v...)))
}

// GetDbConn 获取链接
func GetDbConn() *gorm.DB {
	return db
}

// GetDbConn 获取mysql连接
func init() {
	once.Do(func() {
		lumberjackLogger := &lumberjack.Logger{
			Filename:   "./logs/sql.log",
			MaxSize:    10, // MB
			MaxBackups: 3,
			MaxAge:     28, // days
		}

		writeSyncer := zapcore.AddSync(lumberjackLogger)

		// 创建自定义 Writer
		customWriter := &customWriter{syncer: writeSyncer}

		// 将自定义 Writer 转换为 gorm 日志记录器
		gormLogger := logger.New(
			customWriter,
			logger.Config{
				SlowThreshold: time.Second,
				LogLevel:      logger.Info,
			},
		)
		//字符串模板

		dsnTemplate := "{{.Username}}:{{.Password}}@tcp({{.Host}}:{{.Port}})/{{.Database}}?charset=utf8mb4&parseTime=True&loc=Local"
		tmpl, err := template.New("dsn").Parse(dsnTemplate)
		if err != nil {
			panic(err)
		}

		data := map[string]string{
			"Username": config.GetConfig().Databases[0].User,
			"Password": config.GetConfig().Databases[0].Password,
			"Host":     config.GetConfig().Databases[0].Host,
			"Port":     strconv.Itoa(config.GetConfig().Databases[0].Port),
			"Database": config.GetConfig().Databases[0].Database,
		}

		var dsn bytes.Buffer
		err = tmpl.Execute(&dsn, data)
		if err != nil {
			panic(err)
		}

		dsnString := dsn.String()

		db, getDbConnErr = gorm.Open(mysql.Open(dsnString), &gorm.Config{
			Logger: gormLogger,
		})
		if getDbConnErr != nil {
			panic(getDbConnErr)
			return
		}

		DB, _ := db.DB()
		// 最大连接数
		DB.SetMaxOpenConns(10)
		// 最大空闲连接数 <= 最大连接数
		DB.SetMaxIdleConns(1)
		// 连接数的生命周期, 从创建开始记时，到时间点会被销毁
		DB.SetConnMaxLifetime(time.Hour * 7)
		// 最大空闲时间, 这个链接距离上一次被使用的时间段，超过这个时间则认为该连接已过期
		DB.SetConnMaxIdleTime(time.Hour)
	})
}

/**
 * @Description: 执行查询语句，并将结果映射到结构体中
 * @param dest 结构体指针
 * @param sql 查询语句
 * @param args 参数
 */
func GetListByRaw(dest interface{}, sql string, args ...interface{}) {
	if db == nil {
		logs.Error("Database connection is nil")
	}
	if err := db.Raw(sql, args...).Scan(dest).Error; err != nil {
		logs.Error("Error executing query:", zap.Error(err), zap.Stack("stacktrace"))
	}
}
